package com.youlu.campus.admin.annotation;

import com.youlu.campus.admin.community.entity.ActivityAclConfig;
import com.youlu.campus.admin.community.service.ActivityAclConfigService;
import com.youlu.campus.admin.utils.AuthUtils;
import com.youlu.campus.common.model.GlobalErrorCode;
import com.youlu.campus.common.base.RestResult;
import com.youlu.campus.common.base.RestResultBuilder;
import com.youlu.campus.entity.*;
import com.youlu.campus.service.activity.ActivityQaRuleService;
import com.youlu.campus.service.activity.ActivityRewardRuleService;
import com.youlu.campus.service.activity.ActivityTaskService;
import com.youlu.campus.service.qa.ActivityQaInfoService;
import com.yuelin.infrastructure.quantum.common.QResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @Author dengbo
 * @Date 2021/3/5 5:40 下午
 */
@Order(Ordered.HIGHEST_PRECEDENCE + 2)
@Aspect
@Component
@Slf4j
public class RequestActivityAuthAspect {

    @Autowired
    private ActivityAclConfigService activityAclConfigService;

    @Autowired
    private ActivityTaskService activityTaskService;

    @Autowired
    private ActivityRewardRuleService activityRewardRuleService;

    @Autowired
    private ActivityQaInfoService activityQaInfoService;

    @Autowired
    private ActivityQaRuleService activityQaRuleService;

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 启用请求过滤
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(com.youlu.campus.admin.annotation.RequestActivityAuth)")
    public Object auth(ProceedingJoinPoint joinPoint) throws Throwable {
        String userId = AuthUtils.getUserId();
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        String uri = request.getRequestURI();
        // 判断是否需要校验
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        boolean isRestResult = RestResult.class.isAssignableFrom(method.getReturnType());
        ActivityAclConfig config = activityAclConfigService.getConfig(userId,uri);
        boolean isAdmin = false;
        List<String> aidList = new ArrayList<>();
        if(config != null){
            isAdmin = "all".equals(config.getType());
            if(CollectionUtils.isNotEmpty(config.getActivityIdList())){
                aidList = config.getActivityIdList();
            }
        }
        if(isAdmin){
            return joinPoint.proceed();
        }
        Object resultMessage = getResultMsg(isRestResult,GlobalErrorCode.NOT_CHOICE_ACTIVITY);
        if (joinPoint.getArgs() == null || joinPoint.getArgs().length <= 0) {
            return resultMessage;
        }
        Object param = Arrays.stream(joinPoint.getArgs()).filter(Objects::nonNull).findFirst().orElse(null);
        if (param == null) {
            return resultMessage;
        }
        RequestActivityAuth requestAuth = method.getAnnotation(RequestActivityAuth.class);
        String spel = requestAuth.activityId();
        AuthParamTypeEnum paramType = requestAuth.paramType();
        String msg = requestAuth.message();
        //解析SPEL表达式
        Object val = AnnotationResolverUtil.parseSPEL(spel, method, joinPoint.getArgs());
        if(val == null || StringUtils.isBlank(val.toString())){
            return resultMessage;
        }
        String activityId = null;
        if(AuthParamTypeEnum.DEFAULT.equals(paramType)){
            activityId = val.toString();
        }else if(AuthParamTypeEnum.TASK.equals(paramType)){
            String taskId = val.toString();
            ActivityTaskInfo taskInfo = activityTaskService.findOne(taskId);
            if(taskInfo == null){
                return getResultMsg(isRestResult,GlobalErrorCode.NOT_FOND_TASK);
            }
            activityId = taskInfo.getActivityId();
        }else if(AuthParamTypeEnum.TASK_RULE.equals(paramType)){
            String ruleId = val.toString();
            ActivityRewardRule rewardRule = activityRewardRuleService.findOne(ruleId);
            if(rewardRule == null) {
                return getResultMsg(isRestResult, GlobalErrorCode.NOT_FOND_RULE_INFO);
            }
            activityId = rewardRule.getActivityId();
        }else if(AuthParamTypeEnum.PAY_CONFIG.equals(paramType)){
            String payConfigId = val.toString();
            PayConfigPage configPage = mongoTemplate.findById(payConfigId, PayConfigPage.class);
            if(configPage == null) {
                return getResultMsg(isRestResult, GlobalErrorCode.NOT_FOND_PAY_CONFIG);
            }
            activityId = configPage.getActivityId();
        }else if(AuthParamTypeEnum.PAY_BUTTON.equals(paramType)){
            String payButtonId = val.toString();
            PayButtonConfigPage payButtonConfigPage = mongoTemplate.findById(payButtonId, PayButtonConfigPage.class);
            if(payButtonConfigPage == null) {
                return getResultMsg(isRestResult, GlobalErrorCode.NOT_FOND_PAY_BUTTON);
            }
            String payConfigId = payButtonConfigPage.getPayConfigPageId();
            PayConfigPage configPage = mongoTemplate.findById(payConfigId, PayConfigPage.class);
            if(configPage == null) {
                return getResultMsg(isRestResult, GlobalErrorCode.NOT_FOND_PAY_CONFIG);
            }
            activityId = configPage.getActivityId();
        }else if(AuthParamTypeEnum.QA.equals(paramType)){
            String qaId = val.toString();
            ActivityQaInfo qaInfo = activityQaInfoService.findById(qaId);
            if(qaInfo == null) {
                return getResultMsg(isRestResult, GlobalErrorCode.NOT_FOND_QA);
            }
            activityId = qaInfo.getActivityId();
        } else if(AuthParamTypeEnum.QA_RULE.equals(paramType)){
            String qaRuleId = val.toString();
            ActivityQaRule qaRule = activityQaRuleService.findOne(qaRuleId);
            if(qaRule == null) {
                return getResultMsg(isRestResult, GlobalErrorCode.NOT_FOND_QA);
            }
            activityId = qaRule.getActivityId();
        }
        if(aidList.contains(activityId)){
            return joinPoint.proceed();
        }
        if(!activityAclConfigService.checkActivity(activityId,userId)){
            if(StringUtils.isNotBlank(msg)){
                return getResultMsg(isRestResult,500,msg);
            }
            return getResultMsg(isRestResult,GlobalErrorCode.ACTIVITY_NOT_ACL);
        }
        return joinPoint.proceed();
    }

    public Object getResultMsg(boolean isRestResult,int code,String msg){
        if(isRestResult){
            return RestResultBuilder.builder().code(code).message(msg).build();
        }
        return QResult.fail(code, msg);
    }

    public Object getResultMsg(boolean isRestResult,GlobalErrorCode errorCode){
        if(isRestResult){
            return RestResultBuilder.builder().errorCode(errorCode).build();
        }
        return QResult.fail(errorCode.getCode(), errorCode.getMessage());
    }
}
